﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Data.GameData.Entities;

namespace Data.GameData
{
    public class Game : BaseObject
    {
        // Declare the event using EventHandler<T>
        public static event EventHandler<BasicEventArgs> RaiseNewTurnEvent;

        #region Attributes
        private EGame dbGame;
        private static Dictionary<Guid, Game> gameList = null;
        private static Game activeGame;

        private Guid id;
        private string name;

        private int turnNumber = 0;
        private int currentDay = 1;
        private int currentPulse = 1;
        #endregion

        #region Constructors
        public Game(string gameName)
        {
            dbGame = new EGame();

            id = Guid.NewGuid();
            name = gameName;

            MarkNew();
        }

        public Game(Guid gameId)
        {
            dbGame = new EGame();

            if (gameId != Guid.Empty)
            {
                dbGame = EGame.LoadFromDB(gameId);
                Load();
            }
        }

        private Game(EGame entity)
        {
            this.dbGame = entity;
            Load();
        }
        #endregion

        #region Public Static Properties
        public static Game ActiveGame
        {
            get { return Game.activeGame; }
            set { Game.activeGame = value; }
        }
        
        static public List<Game> Games
        {
            get
            {
                if (gameList == null || gameList.Count == 0) FetchGames();
                return new List<Game>(gameList.Values);
            }
        }

        static public Game GetGame(Guid gameId)
        {
            if (gameList == null || gameList.Count == 0) FetchGames();

            if (gameList.ContainsKey(gameId))
                return gameList[gameId];

            return null;
        }

        static public Game[] GetGames()
        {
            return Games.ToArray();
        }
        #endregion

        #region Public Properties
        public Guid Id
        {
            get { return id; }
            set { }
        }

        public string Name
        {
            get { return name; }
            set
            {
                if (name == value) return;
                name = value;
                IsDirty = true;
            }
        }

        public int TurnNumber
        {
            get { return turnNumber; }
            set
            {
                if (turnNumber == value) return;
                turnNumber = value;
                IsDirty = true;
                OnRaiseNewTurnEvent(new BasicEventArgs());
            }
        }

        public int CurrentDay
        {
            get { return currentDay; }
            set 
            {
                if (currentDay == value) return;
                currentDay = value;
                IsDirty = true;
            }
        }

        public int CurrentPulse
        {
            get { return currentPulse; }
            set 
            {
                if (currentPulse == value) return;
                currentPulse = value;
                IsDirty = true;
            }
        }
        #endregion

        #region Static Private Methods
        static private void FetchGames()
        {
            if (gameList == null || gameList.Count == 0)
            {
                gameList = new Dictionary<Guid, Game>();

                List<EGame> gameEntityList = EGame.GetAll();
                lock (gameList)
                {
                    // Create the Plan business objects
                    foreach (EGame gameEntity in gameEntityList)
                    {
                        Game game = new Game(gameEntity);
                        gameList.Add(game.Id, game);
                    }
                }
            }
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Check if all races in the game have completed their turn.
        /// </summary>
        /// <returns>If all are complete</returns>
        public bool TurnComplete()
        {
            bool r = true;
            foreach (Race race in Race.Races)
            {
                if (race.TurnComplete == false)
                    r = false;
            }
            return r;
        }

        public override string ToString()
        {
            return Name;
        }
        #endregion

        #region BaseObject overrides - Save, Update, etc
        public override void Save()
        {
            // Save links this is dependent on
            StarSystem.SaveAll();

            // Save this
            base.Save();

            // Save link dependent on this
            Race.SaveAll();
        }

        internal override void Update()
        {
            if (MarkedToDelete)
            {
                // Delete dependencies

                DeleteNow();
            }
            else //if(IsNew || IsDirty)
            {
                if (IsNew)
                {
                    if (gameList == null) gameList = new Dictionary<Guid, Game>();
                    gameList.Add(this.Id, this);
                }

                SaveValues();
                dbGame.SaveToDB(IsNew);
            }
        }

        internal override void DeleteNow()
        {
            if (dbGame.Values == null)
                dbGame.Values = new Stitch.Database.DBRow();

            dbGame.Values["id"] = id;

            dbGame.Delete();

            //
            gameList.Remove(id);
            MarkDeleted();
        }

        internal override void SaveValues()
        {
            if (dbGame.Values == null)
                dbGame.Values = new Stitch.Database.DBRow();

            dbGame.Values["id"] = id;
            dbGame.Values["name"] = name;
            dbGame.Values["turnNumber"] = turnNumber;
            dbGame.Values["day"] = currentDay;
            dbGame.Values["pulse"] = currentPulse;
        }

        internal override void LoadValues()
        {
            if (dbGame.Values == null)
                return;

            id = dbGame.Values["id"];
            name = dbGame.Values["name"];
            turnNumber = dbGame.Values["turnNumber"];
            currentDay = dbGame.Values["day"];
            currentPulse = dbGame.Values["pulse"];
        }

        internal override void InitialiseObject()
        {
        }
        #endregion

        #region Protected Event Managers
        protected virtual void OnRaiseNewTurnEvent(BasicEventArgs e)
        {
            // Make a temporary copy of the event to avoid possibility of
            // a race condition if the last subscriber unsubscribes
            // immediately after the null check and before the event is raised.
            EventHandler<BasicEventArgs> handler = RaiseNewTurnEvent;

            // Event will be null if there are no subscribers
            if (handler != null)
            {
                e.Ident = Guid.Empty;
                e.EventType = BasicEventType.eGAME;
                e.Operation = BasicOperationType.eMODIFY;

                // Use the () operator to raise the event.
                handler(this, e);
            }
        }
        #endregion
    }
}
